import random
import copy
import locale
import operator
import defaultvalue
import astar
import blackboard
import minisprite
import driver
import searchalg
import datamap
import gamesprite

##################################################
# receive a tanker object, give him orders
##################################################
class GameAI:
    def __init__(self, black_board):
        self.status = 0
        self.blackboard = black_board
        self.map = []
        self.way = []
        # produce different search way algorithms
        self.shotblocks_enemy = astar.AStar()
        self.shotblocks_enemy.set_shootblocks(True)
        self.astar = astar.AStar()
        self.turn_al = searchalg.SearchAlg(defaultvalue.default_turnsofenemy2)
        self.evade_player = searchalg.SearchAlg(defaultvalue.default_turnsofenemy1)
        self.evade_player.set_reverse(True)
        self.random_move = searchalg.SearchAlg(defaultvalue.default_turnsofenemy0)
        
    def load_date(self, tanker, tanker_pos, destination, player_pos):
        """ load parameters for the algorithms """        
        self.tanker = tanker
        self.start_position = [tanker_pos[1], tanker_pos[0]]
        # choose one destination from a list of destinations
        temp_destination = destination[random.randint(0, len(destination) - 1)]
        self.destination = [temp_destination[1], temp_destination[0]]
        # choose one player position from a list of players
        temp_playerPos = player_pos[random.randint(0, len(player_pos) - 1)]
        self.player_pos = [temp_playerPos[1], temp_playerPos[0]]        

    def init_map(self,level):
        """ change the map into certain format """
        # map for normal tanks
        map_dic = { gamesprite.Enemy: 10,
                    gamesprite.Steel: 10,
                    gamesprite.River: 10,
                    gamesprite.Brick: 10 }
        # map for tanks that want to shoot bricks
        if level == 5 or level == 6:
            map_dic[gamesprite.Enemy] = 1000
            map_dic[gamesprite.Steel] = 1000
            map_dic[gamesprite.River] = 1000
            map_dic[gamesprite.Brick] = 1
        temp_map = self.tanker.datamap.get_simplemap(map_dic)
        self.height = len(temp_map[0])
        self.length = len(temp_map)
        changed_map = []
        for i in xrange(2):
            for j in xrange(2):
                temp_map[self.start_position[0] + i][self.start_position[1] + j] = 0
                temp_map[self.destination [0] + i][self.destination[1] + j] = 0
                if level == 1: # want to evade player
                    temp_map[self.player_pos[0] + i][self.player_pos[1] + j] = 10
        # deal with the blocks around the destination
        x = self.destination[0]
        y = self.destination[1]
        if x - defaultvalue.default_xbias >= 0 :
            x -= defaultvalue.default_xbias
        else:
            x = 0
        if y - defaultvalue.default_ybias >= 0:
            y -= defaultvalue.default_ybias
        else:
            y = 0
        for line_index in xrange(4):
            if x + line_index >= self.length: # reach the verge
                break
            for row_index in xrange(6):
                if y + row_index >= self.height :
                    break
                else:
                    temp_map[x + line_index][y + row_index] = 0                
        # judge if the tanker can move to a certain place        
        for x in xrange(self.length - 1):
            line = []
            for y in xrange(self.height - 1):
                value = temp_map[x][y] + temp_map[x][y + 1] + temp_map[x + 1][y] + temp_map[x + 1][y + 1]
                line.append(value)
            changed_map.append(line)
        self.map = copy.deepcopy(changed_map)
        self.height = len(self.map[0])
        self.length = len(self.map)

    def cal_distance(self, position1, position2):
        """ calculate Manhattern distance"""
        return operator.abs(position1[0] - position2[0]) \
               + operator.abs(position1[1] - position2[1])

    def switch_status(self, level):
        """ different things during different status """
        # distance to player and distance to destination
        # different and important tanks
        if level >= 3:
            self.decide(level) # normal action
        # not important tanks, begin to do mean thins
        else:
            chase_player,attack_base = self.blackboard.get_status()
            dis1 = self.cal_distance(self.start_position, self.player_pos)
            dis2 = self.cal_distance(self.start_position, self.destination)
            if self.status == 0: # move to its default destination
                if chase_player == True and attack_base == True:
                    if dis1 < dis2 and dis1 < defaultvalue.default_actdistance:
                        self.status = 1
                    elif dis2 < dis1 and dis2 < defaultvalue.default_actdistance:
                        self.status = 2
                    else:
                        self.decide(level)
                elif chase_player == True and dis1 < defaultvalue.default_actdistance:
                    self.status = 1
                elif attack_base == True and dis2 < defaultvalue.default_actdistance:
                    self.status = 2
                else:
                    self.decide(level)
            if self.status == 1 : # begin to chase player
                # if chase time use out or lose player, turn back to default move
                if chase_player == False or dis1 > defaultvalue.default_actdistance:
                    self.status = 0
                    self.decide(level)
                else:
                    # not attack base and in effect area
                    level = 3
                    self.decide(level)
            if self.status == 2 : # begin to attack base
                if attack_base == False:
                    self.status = 0
                    self.decide(level)
                else:
                    level = 4
                    self.decide(level)            
        
    def decide(self, level):
        """ decide the smart levels of the tanker, and use certain algorithm """
        if level == 6:
            start_position = self.start_position
            end_position = self.player_pos
            self.search_way(self.shotblocks_enemy, start_position, end_position)
        if level == 5:
            start_position = self.start_position
            end_position = self.destination
            self.search_way(self.shotblocks_enemy, start_position, end_position)
        if level == 4:
            start_position = self.start_position
            end_position = self.destination
            self.search_way(self.astar, start_position, end_position)
        if level == 3:
            start_position = self.start_position
            end_position = self.player_pos
            self.search_way(self.astar, start_position, end_position)
        if level == 2:
            start_position = self.start_position
            end_position = self.player_pos
            self.turn_al.set_destination(end_position)
            self.search_way(self.turn_al, start_position, end_position)
        if level == 1:
            # player is too close, fight with the player
            if self.cal_distance(self.start_position, self.player_pos)< 10:
                start_position = self.start_position
                end_position = self.player_pos
                self.search_way(self.astar, start_position, end_position)
            # player is still far away, run to evade him
            else :
                start_position = self.start_position
                end_position = self.player_pos
                self.evade_player.set_destination(end_position)
                self.search_way(self.evade_player, start_position, end_position)
        if level == 0:
            start_position = self.start_position
            end_position = self.destination
            self.search_way(self.random_move, start_position, end_position)
        
    def search_way(self, search_al , start_position , end_position):
        self.way = search_al.search(self.map, start_position, end_position)

    def update(self, tanker, tankerpos, destination, player_pos, level):
        """ search for a route and send fire orders"""
        self.load_date(tanker, tankerpos, destination, player_pos)
        self.init_map(level)
        self.switch_status(level)
        self.tanker.driver.clear(driver.PathSignal.KIND)        
        # send move orders
        for index in xrange(len(self.way)):
            self.tanker.driver.send(driver.PathSignal(self.way[index], False))
        # send fire orders
        probability = 1 #0.55 + (level + 1) *0.06        
        if random.uniform(0, 1)< probability:
            self.tanker.fire()

    def default_start(self):
        """ default start, creat a map for itself """
        blocks = defaultvalue.defaultBlockNum
        self.length = defaultvalue.defaultLength
        self.height = defaultvalue.defaultHeight
        self.start_position = [0, 0]
        self.destination = [ self.length-1, self.height-1 ]
        # fill map with blank
        for x in xrange(self.length):
            input = []
            for y in xrange(self.height):
                input.append(0)
            self.map.append(input)
        # join start position and destination
        while 1:
            x = random.randint(0, self.length-1)
            y = random.randint(0, self.height-1)
            if x == self.destination[0] and y == self.destination[1] :
                pass
            elif x == self.start_position[0] and y == self.start_position[1] :
                pass
            else:
                self.player_pos= [x , y]
                break
        # join in blocks
        while blocks > 0:
            blocks -= 1
            x = random.randint(0, self.length-1)
            y = random.randint(0, self.height-1)
            if x == self.destination[0] and y == self.destination[1] :
                blocks += 1
            elif x == self.start_position[0] and y == self.start_position[1] :
                blocks += 1
            elif self.map[x][y] == 0:
                self.map[x][y] = 1         

    def print_map(self):
        print 'The map is:\n'
        for x in xrange(self.length):
            for y in xrange(self.height):
                if x == self.start_position[0] and y == self.start_position[1]: # print start
                    print 'S\t'
                elif x == self.destination[0] and y == self.destination[1]: # print end
                    print 'E\t'
                elif x == self.player_pos[0] and y == self.player_pos[1]: # print player
                    print 'P\t'
                else:
                    print locale.str(self.map[x][y])+'\t'
            print '\n'

    def print_way(self):
        print 'start at:'
        print self.start_position
        print '\n'
        print 'destination is :'
        print self.destination
        print '\n'
        print 'The route is:\n'
        dicDirection = { 0:"Up",
                         1:"Left",
                         2:"Down",
                         3:"Right"}
        for index in xrange(len(self.way)):
            print dicDirection[self.way[index]] + "\t"            
        print '\n'
